﻿using ConverterSystems.ServiceModel;
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Net.Security;
using System.Runtime.CompilerServices;

namespace ConverterSystems.TestClient
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            // Step 1 - Create a config.  These settings will generate a certificate if neccessary.
            var config = new ApplicationConfiguration()
            {
                ApplicationName = "ConverterSystems.TestClient",
                ApplicationType = ApplicationType.Client,
                SecurityConfiguration = new SecurityConfiguration { ApplicationCertificate = new CertificateIdentifier { StoreType = @"Windows", StorePath = @"CurrentUser\My", SubjectName = Utils.Format(@"CN={0}, DC={1}", "ConverterSystems.TestClient", System.Net.Dns.GetHostName()) }, TrustedPeerCertificates = new CertificateTrustList { StoreType = @"Windows", StorePath = @"CurrentUser\TrustedPeople", }, NonceLength = 32, AutoAcceptUntrustedCertificates = true },
                TransportConfigurations = new TransportConfigurationCollection(),
                TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
                ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
            };
            config.Validate(ApplicationType.Client);
            config.EnsureApplicationCertificate();
            if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
            {
                config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted); };
                ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => { return (d == SslPolicyErrors.None) || (d == SslPolicyErrors.RemoteCertificateChainErrors); };
            }

            // Step 2 - Create a session with your server.
            using (var session = new UaSession(config, "opc.tcp://" + Dns.GetHostName() + ":51212"))
            {
                // Step 3 - Create a model having a property for each item you wish to monitor
                var model = new Model(session);
                model.PropertyChanged += (s, e) => Console.WriteLine("{0}: {1}", e.PropertyName, s.GetType().GetProperty(e.PropertyName).GetValue(s));
                Console.WriteLine("Press any key to exit...");
                Console.ReadKey(true);
            }
        }
    }

    internal class Model : INotifyPropertyChanged
    {
        public Model(UaSession session)
        {
            // Step 4 - Create a subscription. Choose faster publishing interval if you wish.
            var subscription = new Subscription(session.DefaultSubscription) { PublishingInterval = 250 };
            // Step 5 - Configure the subsscription with all the [MonitoredItem] attributes found in the model
            subscription.WithModel(this).AddBindings();
            // Step 6 - Add the subscription to the session.
            session.AddSubscription(subscription);
        }

        /// <summary>
        /// Gets or sets the value of Robot1Axis1.
        /// </summary>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1", RelativePath = "2:Axis1")]
        public Single Robot1Axis1
        {
            get { return _Robot1Axis1; }
            set { SetProperty(ref _Robot1Axis1, value); }
        }

        private Single _Robot1Axis1;

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (!object.Equals(storage, value))
            {
                storage = value;
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
}